package com.efivestar.idm.api.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.efivestar.idm.api.common.Cons;
import com.efivestar.idm.api.domain.DeviceDomain;
import com.efivestar.idm.api.exception.ErrorParamException;
import com.efivestar.idm.api.exception.NotSupportMethodHandlerException;
import com.efivestar.idm.api.service.lock.protocol.handler.LockProtocolHanderlIntf;
import com.efivestar.idm.api.util.ByteUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;
import java.util.Map;
import java.util.Set;

public class NettyHttpServerHandler extends ChannelInboundHandlerAdapter {
    private static Logger logger = LoggerFactory.getLogger(NettyHttpServerHandler.class);

    private HttpRequest request;
    private FullHttpResponse response;
    private FullHttpRequest fullRequest;

    private static final String SUCCESS = "success";
    private static final String ERROR = "error";
    private static final String CONNECTION_KEEP_ALIVE = "keep-alive";
    private static final String CONNECTION_CLOSE = "close";

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        try {
            // 返回loT平台HTTP状态码默认为200
            HttpResponseStatus status = HttpResponseStatus.OK;

            if (msg instanceof FullHttpRequest) {
                request = (HttpRequest) msg;
                fullRequest = (FullHttpRequest) msg;
                String jsonStr = fullRequest.content().toString(CharsetUtil.UTF_8);
                if (jsonStr != null) {
                    try {
                        int index = jsonStr.lastIndexOf("\n");
                        if (index != -1) {
                            jsonStr = jsonStr.substring(index);
                        }
                    } catch (Exception e) {
                        jsonStr = "";
                        status = HttpResponseStatus.INTERNAL_SERVER_ERROR;
                        logger.error("get responseJsonBody fail !");
                    }
                } else {
                    jsonStr = "";
                }
                logger.info("=====接收到平台推送的消息,次数[{}]=====：{}", NettyHttpServer.msgCnt, jsonStr);
                NettyHttpServer.msgCnt++;
                // 适配消息处理器
                JSONObject json = JSON.parseObject(jsonStr);
                if (json != null && NotifyType.DEVICE_DATA_CHANGED.equals(json.getString("notifyType"))) {// 数据变化事件
                    String passthroughdata = json.getJSONObject("service").getJSONObject("data").getString("passthroughdata");
                    ByteBuf content = Unpooled.copiedBuffer(ByteUtils.hexToBytes(passthroughdata));
                    content.readerIndex(0);
                    int length = content.readableBytes();
                    byte bts[] = new byte[length];
                    content.getBytes(0, bts);
                    try {
                        byte[] head = new byte[2];
                        byte[] command = new byte[1];
                        content.getBytes(0, head, 0, 2);
                        //当智能锁发的包是ef02代表是远程开锁的回复，所以没有指令码.
                        if (!"ef02".equalsIgnoreCase(ByteUtils.bytes2hex(head))) {
                            content.getBytes(9, command, 0, 1);
                        }
                        LockProtocolHanderlIntf protocolHandler = getProtocolHandler(head, command);
                        if (protocolHandler != null) {
                            byte[] executeResult = protocolHandler.execute(content);
                            if (executeResult == null) {
                                throw new NullPointerException();
                            }
                        } else {
                            // 没有能处理该消息的处理器
                            throw new NotSupportMethodHandlerException();
                        }
                    } catch (Exception ex) {
                        status = HttpResponseStatus.INTERNAL_SERVER_ERROR;
                        if (ex instanceof NotSupportMethodHandlerException) {
                            logger.error("找不到消息处理器:{}", ByteUtils.bytes2hex(bts));
                        } else if (ex instanceof IndexOutOfBoundsException) {
                            logger.error("找不到消息处理器:{}", ByteUtils.bytes2hex(bts));
                        } else if (ex instanceof ErrorParamException) {
                            logger.error("参数错误:{}", ByteUtils.bytes2hex(bts));
                        } else if (ex instanceof NullPointerException) {
                            logger.error("信息处理返回null,指令码：{}， 异常：{}", new Object[]{ByteUtils.bytes2hex(bts), ex});
                        } else {
                            logger.error("其他错误,指令码：{}， 异常：{}", new Object[]{ByteUtils.bytes2hex(bts), ex});
                        }
                    }
                } else if (json != null && NotifyType.DEVICE_ADDED.equals(json.getString("notifyType"))) {// 设备添加事件
                    try {
                        logger.info("************收到添加设备通知*************");
                        String iotDeviceId = json.getString("deviceId");
                        String deviceId = json.getJSONObject("deviceInfo").getString("nodeId");
                        if (StringUtils.isBlank(iotDeviceId) || StringUtils.isBlank(deviceId)) {
                            logger.info("设备添加无法获取iotDeviceId[{}]  deviceId[{}]", iotDeviceId, deviceId);
                            return;
                        } else {
                            DeviceDomain deviceDomain = DeviceDomain.builder().deviceId(deviceId).iotDeviceId(iotDeviceId).heartTime(new Date())
                                    .iccid("").type(Cons.LOCK).defenseStatus(Cons.DEFENSE_OFF_STATUS).verifyEndPoint(1).createTime(new Date()).build();
                            if (NettyHttpServer.deviceService.addDevice(deviceDomain) > 0) {
                                logger.info("添加设备成功！iotDeviceId[{}]  deviceId[{}]", iotDeviceId, deviceId);
                            } else {
                                logger.info("添加设备失败！iotDeviceId[{}]  deviceId[{}]", iotDeviceId, deviceId);
                            }
                        }
                    } catch (Exception e) {
                        logger.error("添加设备异常：", e);
                        status = HttpResponseStatus.INTERNAL_SERVER_ERROR;
                    }

                }
            }
            logger.info("返回HttpResponseStatus状态[{}]", status);
            if (status.code() == 200) {
                writeResponse(ctx.channel(), status, SUCCESS, false);
            } else {
                writeResponse(ctx.channel(), status, ERROR, true);
            }
        } finally {
            ReferenceCountUtil.release(msg);
        }
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        logger.info("HttpServerInboundHandler.channelReadComplete");
        ctx.flush();
    }

    /**
     * 响应报文处理
     *
     * @param channel    当前上下文Channel
     * @param status     响应码
     * @param msg        响应消息
     * @param forceClose 是否强制关闭
     */
    private void writeResponse(Channel channel, HttpResponseStatus status, String msg, boolean forceClose) {
        ByteBuf byteBuf = Unpooled.wrappedBuffer(msg.getBytes());
        response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, byteBuf);
        boolean close = isClose();
        if (!close && !forceClose) {
            response.headers().add(org.apache.http.HttpHeaders.CONTENT_LENGTH, String.valueOf(byteBuf.readableBytes()));
        }
        ChannelFuture future = channel.write(response);
        if (close || forceClose) {
            future.addListener(ChannelFutureListener.CLOSE);
        }
    }

    private boolean isClose() {
        if (request.headers().contains(org.apache.http.HttpHeaders.CONNECTION, CONNECTION_CLOSE, true) ||
                (request.protocolVersion().equals(HttpVersion.HTTP_1_0) &&
                        !request.headers().contains(org.apache.http.HttpHeaders.CONNECTION, CONNECTION_KEEP_ALIVE, true)))
            return true;
        return false;
    }

    /**
     * @param head
     * @param command
     * @return
     * @throws
     * @Title: getProtocolHandler
     * @Description: 根据具体协议返回对应的处理器
     * @author LiMiao
     * @date 2018年2月20日 下午2:02:20
     */
    private LockProtocolHanderlIntf getProtocolHandler(byte[] head, byte[] command) {
        Set<Map.Entry<String, LockProtocolHanderlIntf>> entries = NettyHttpServer.lockProtocolHanderlIntfMap.entrySet();
        for (Map.Entry<String, LockProtocolHanderlIntf> entry : entries) {
            LockProtocolHanderlIntf protocolHander = entry.getValue();
            if (protocolHander.match(head, command)) {
                return protocolHander;
            }
        }
        return null;
    }
}  